package org.autumn.review.thread;

import lombok.extern.slf4j.Slf4j;
import org.autumn.thread.ThreadState;

import java.sql.Time;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * test some thread method
 */
@Slf4j
public class ThreadMethod {
    public static void main(String[] args) throws InterruptedException {
        threadStatus();
    }

    /**
     * at cpu level thread status
     *      new
     *      ready
     *      running
     *      block
     *      terminated
     * at java level thread status
     *      new
     *      runnable
     *      blocking
     *      waiting
     *      timed-waiting
     *      terminated
     */
    private static void threadStatus() throws InterruptedException {
        Thread t1 = new Thread(() -> {
        }, "t1");

        Thread t2 = new Thread(() -> {
            while (true){}
        }, "t2");
        t2.start();
        Thread t3 = new Thread(() -> {
        }, "t3");
        t3.start();
        Thread t4 = new Thread(() -> {
            try {
                synchronized (ThreadState.class){
                    TimeUnit.HOURS.sleep(1);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t4");

        t4.start();
        Thread t5 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t5");

        t5.start();
        Thread t6 = new Thread(() -> {
            synchronized (ThreadState.class){
            }
        }, "t6");

        t6.start();

        TimeUnit.SECONDS.sleep(1);

        /**
         * t1 state ->NEW
         * t2 state ->RUNNABLE
         * t3 state ->TERMINATED
         * t4 state ->TIMED_WAITING
         * t5 state ->WAITING
         * t6 state ->BLOCKED
         */
        log.info("t1 state ->{}" + t1.getState());
        log.info("t2 state ->{}" + t2.getState());
        log.info("t3 state ->{}" + t3.getState());
        log.info("t4 state ->{}" + t4.getState());
        log.info("t5 state ->{}" + t5.getState());
        log.info("t6 state ->{}" + t6.getState());
    }

    /**
     * main thread terminated all Daemon thread stop
     * @throws InterruptedException
     */
    private static void testDaemon() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true){
                log.info("running ...");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1");
        t1.setDaemon(true);
        t1.start();

        TimeUnit.SECONDS.sleep(3);
    }

    /**
     * this method will stop thread when the thread interrupt flag is false
     */
    private static void testPark() {
        Thread t1 = new Thread(() -> {
            log.info("park ...");
            LockSupport.park();
            log.info("upPark ...");
            // this method will not modify interrupt flag to false,just get
//            log.info("Thread.currentThread().isInterrupted() -> {}", Thread.currentThread().isInterrupted());
            // this method will modify interrupt flag to false
            log.info("Thread.interrupted() -> {}", Thread.interrupted());

            // if interrupt flag is ture,park method will not take effect
            LockSupport.park();
            log.info("mk");
        }, "t1");
        t1.start();


        t1.interrupt();
    }

    /**
     * twoPhaseTerminated Stop the thread gracefully
     *
     * @throws InterruptedException
     */
    private static void twoPhaseTerminated() throws InterruptedException {
        Thread monitor = new Thread(() -> {
            while (true) {
                Thread current = Thread.currentThread();
                if (current.isInterrupted()) {
                    log.info("stop...");
                    break;
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                    log.info("monitoring ...");
                } catch (InterruptedException e) {
                    log.info("Interrupted in sleep ...");
                    // mark interrupt flag to true
                    current.interrupt();
                }
            }
        }, "monitor");
        monitor.start();
        TimeUnit.SECONDS.sleep(3);
        monitor.interrupt();
    }

    /**
     * if you interrupt a running thread, this thread interrupt flag
     * will be modified to ture,Interrupted thread can be process by
     * marking -> interrupt flag
     */
    private static void testInterruptRunning() {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    log.info("i was interrupted ... exit loop");
                    break;
                }
            }
        }, "t1");
        t1.start();
        log.info("main interrupt t1 ...");
        t1.interrupt();
        log.info("t1.isInterrupted() -> {}", t1.isInterrupted());
    }

    /**
     * call yield(), current thread will give up execution
     * waiting to allocate time slice
     */
    private static void testYield() {
        Thread t1 = new Thread(() -> {
            int count = 0;
            while (true) {
                System.out.println("t1->" + count++);
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(() -> {
            int count = 0;
            while (true) {
                Thread.yield();
                System.out.println("\t \t \t t2->" + count++);
            }
        }, "t2");
        t2.start();
    }

    /**
     * call interrupt() ,if thread status is timed waiting
     * the thread will throw InterruptedException
     *
     * @throws InterruptedException
     */
    private static void testInterrupt() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                log.info("t1 sleep ...");
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                log.info("t1 InterruptedException ...");
            }
        }, "t1");
        t1.start();
        log.info("t1.getState() = {}", t1.getState());
        TimeUnit.SECONDS.sleep(1);
        log.info("t1.getState() = {}", t1.getState());
        t1.interrupt();
        TimeUnit.SECONDS.sleep(1);
        log.info("t1.getState() = {}", t1.getState());
    }

    /**
     * call sheep method ,the thread status will change timed waiting
     *
     * @throws InterruptedException
     */
    private static void testSleep() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                log.info("t1 sleep ...");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");

        t1.start();

        log.info("t1.getState() = {}", t1.getState());
        TimeUnit.SECONDS.sleep(1);
        log.info("t1.getState() = {}", t1.getState());
    }


    /**
     * test join
     *
     * @throws InterruptedException
     */
    private static void testJoin() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                log.info("t1 end ...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();

        t1.join();
        log.info("main end ...");
    }
}
